-module(runtests).
-compile([export_all]).

tests() -> [{"printf", printf:expected()},
            {"types0", types0:expected()},
            {"types", types:expected()},
            {"sizeof", ""},
            {"error", ""}].

frmt(Str, Args) ->
    binary_to_list(iolist_to_binary(io_lib:format(Str, Args))).

frmt_cmd(BinDir, Module, A) ->
    frmt("erl +A ~w -pa build -pa ../ebin -pa ~s -noshell -run ~s main",
         [A, BinDir, Module]).

cmd_erl(M, A) -> os:cmd(frmt_cmd("../bin", M, A)).
cmd_erl_debug(M, A) -> os:cmd(frmt_cmd("../bin.debug", M, A)).

getmem(X, [X|_]) -> X;
getmem(X, [{X, Y}|_]) -> {X, Y};
getmem(X, [_|Rest]) -> getmem(X, Rest).

main() ->
    run([[{dir, "../bin"}],
         [{dir, "../bin"}, {thread_pool, 4}]],
        tests()).

main(["debug"]) ->
    run([[{dir, "../bin.debug"}],
         [{dir, "../bin.debug"}, {thread_pool, 4}]],
        tests()).

getval(_Key, [], Default) -> Default;
getval(Key, [{Key, Value}|_Rest], _Default) -> Value;
getval(Key, [_|Rest], Default) -> getval(Key, Rest, Default).

getval(Key, [{Key, Value}|_Rest]) -> Value;
getval(Key, [_|Rest]) -> getval(Key, Rest).

run_erl(Erl_Options, Module) ->
    ThreadPoolSize = getval(thread_pool, Erl_Options, 0),
    Dir = getval(dir, Erl_Options),
    Cmd = frmt_cmd(Dir, Module, ThreadPoolSize),
    os:cmd(Cmd).

run(Erl_Options, Tests) ->
    {Passed, Total, Failed} = do_tests_list(Erl_Options, Tests),
    io:format("~p/~p\n", [Passed, Total]),
    case Passed of 
        Total ->
            io:format("OK\n");
        _ ->
            io:format("FAILED: ~p\n", [Failed])
    end,
    util:die(Total - Passed).

do_tests_list(Erl_Options_List, Tests) ->
    do_tests_list(Erl_Options_List, Tests, {0, 0, []}).

do_tests_list([], _Tests, {P, T, F}) -> {P, T, F};

do_tests_list([Erl_Options|Rest], Tests, {P, T, F}) ->
    {Passed, Total, Failed} = do_tests(Erl_Options, Tests),
    do_tests_list(Rest, Tests, {P+Passed, T+Total, F++Failed}).

erl(Erl_Options, Module) ->
    Self = self(),
    Pid = spawn_link(fun() ->
                             Self ! {res, run_erl(Erl_Options, Module)}
                     end),
    receive
        {res, X} -> {ok, util:lines(X)}
    after 10000 ->
        exit(Pid, kill),
        %% also, need to kill os process
        timeout
    end.

join([]) -> "";
join([X|Rest]) -> X ++ "\n" ++ join(Rest).

pformat_alist([]) -> "";

pformat_alist([{Key, Val}|Rest]) ->
    frmt("~p=~p", [Key, Val]) ++ " " ++ pformat_alist(Rest).

do_tests(_Fun, [], Passed, Total, Failed) ->
    {Passed, Total, Failed};

do_tests(Fun, [{File, Exp}|Tests], Passed, Total, Failed) when is_list(File) ->
    io:format("~s ~s\n", [File, pformat_alist(Fun)]),
    case erl(Fun, File) of
        {ok, Exp} ->
            do_tests(Fun, Tests, Passed+1, Total+1, Failed);
        Error ->
            case Error of
                {ok, NotE} when is_list(NotE) ->
                    io:format("~s: unexpected output:\n~p\nexpected:\n~p\n",
                              [File, NotE, Exp]);
                _ -> io:format("~s: got error:\n~p\nexpected:\n~p\n",
                               [File, Error, Exp])
            end,    
            do_tests(Fun, Tests, Passed, Total+1, [{File, Fun}|Failed])
    end.

do_tests(Fun, Tests) -> do_tests(Fun, Tests, 0, 0, []).
